home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / mail / mm / mm-0.90 / file.c < prev    next >
Encoding:
C/C++ Source or Header  |  1990-12-18  |  30.1 KB  |  1,124 lines

  1. /*
  2.  * Copyright (c) 1986, 1990 by The Trustees of Columbia University in
  3.  * the City of New York.  Permission is granted to any individual or
  4.  * institution to use, copy, or redistribute this software so long as it
  5.  * is not sold for profit, provided this copyright notice is retained.
  6.  */
  7.  
  8. /* XXXXXXXX  memory leaks at most all the cmerr's in here */
  9.  
  10. #ifndef lint
  11. static char *rcsid = "$Header: /f/src2/encore.bin/cucca/mm/tarring-it-up/RCS/file.c,v 2.19 90/10/04 18:24:13 melissa Exp $";
  12. #endif
  13.  
  14. /*
  15.  * file.c - routines for handling mail files
  16.  */
  17.  
  18. #include "mm.h"
  19. #include "parse.h"
  20. #include "cmds.h"
  21. #include "rd.h"
  22.  
  23. msgvec *cf = nil;            /* current mail file */
  24.  
  25. msgvec *getfile ();
  26.  
  27. /*
  28.  * check_cf:
  29.  * see if we have a mail file we're looking at
  30.  * if they want to modify it, see if they can and give warning if not
  31.  * if we're processing command line arguments, they must have done one
  32.  * that requires their mail_file, so get it if we have no file
  33.  * The modify parameter can be one of:
  34.  *   O_RDONLY    - need a mail file
  35.  *   O_WRONLY   - need a writeable file
  36.  *   O_RDWR     - want a writeable file, but will settle for read-only
  37.  *                e.g. this is used by cmd_read when called due to 
  38.  *          command line args, or for pre-checking writeable
  39.  *          files (we'll panic later, during O_WRONLY check)
  40.  */
  41. check_cf (modify)
  42. int modify;
  43. {
  44.     extern int gotargs;
  45.     int writeable = false;
  46.  
  47.     writeable = (modify == O_WRONLY) || (modify == O_RDWR);
  48.  
  49.     if (cf == nil) {
  50.     if (!gotargs)
  51.         return sorry ("No current mail file");
  52.     if (strlen(mail_file) != 0) {    /* want a file, get one */
  53.         printf("Reading %s ...\n", mail_file);
  54.         cf = getfile(mail_file, writeable ? CMD_GET : CMD_EXAMINE);
  55.         if (cf == NULL && modify == O_RDWR)
  56.         cf = getfile (mail_file, CMD_EXAMINE);
  57.         if (cf == NULL)
  58.         return sorry ("No current mail file");
  59.         do_flagged();        /* show flagged ones */
  60.     } /* continue with other checks */
  61.     }
  62.     if (!(writeable && (cf->flags & MF_RDONLY))) /* don't want to change, */
  63.                          /*   or can */
  64.     return true;
  65.     if ((modify_read_only == SET_ALWAYS) || (modify == O_RDWR))
  66.     return true;
  67.     if (modify_read_only == SET_ASK)
  68.     if (yesno("File is read-only; modify anyway? ", "no"))
  69.         return true;
  70.     return sorry ("Can't modify a file visited with the EXAMINE command");
  71. }
  72.  
  73.  
  74. /*
  75.  * cmd_get:
  76.  * get (or "examine") a file, releasing the old one if we get the new
  77.  * one successfully.   
  78.  */
  79. int
  80. cmd_get (n)
  81. int n;
  82. {
  83.     msgvec *nf, *f;
  84.     char *fname, *parse_output_file(), *parse_input_file();
  85.     extern int gotargs;            /* are we processing command line? */
  86.     int i;
  87.     buffer filename;
  88.     extern top_level_parser();
  89.  
  90.     gotargs = FALSE;            /* don't exit after get in cmd line */
  91.     noise ("mail file");
  92.     if (n == CMD_GET)
  93.     fname = parse_output_file ("mail file", mail_file, true);
  94.     else
  95.     fname = parse_input_file ("mail file", mail_file, true);
  96.     confirm ();                /* XXX leak */
  97.  
  98.     if (cf)                /* save it before reading new file */
  99.     if (!update (&cf,UPD_SAVEMOD) && (cf->flags & MF_WRITERR)) {
  100.         fprintf (stderr,
  101.              "?Cannot save old file -- %s command aborted.\n",
  102.              n == CMD_EXAMINE ? "EXAMINE" : "GET");
  103.         free (fname);
  104.         return (false);
  105.     }
  106.  
  107.     nf = getfile(fname, n);
  108.  
  109.     (void) sprintf(filename, "%s.rc", fname); /* setup filename.rc */
  110.  
  111.     free (fname);
  112.     if (nf == nil)
  113.     return false;
  114.  
  115.     if (cf && (cf != nf)) {        /* get rid of old current file */
  116.     f = cf;                /* remember this another minute... */
  117.     cf = nil;            /* ...but don't update it anymore */
  118.     (*msg_ops[f->type].close)(f->filep); /* close old file */
  119.     msgvec_free (f);        /* free all the bits and pieces */
  120.     }
  121.  
  122.     cf = nf;                /* make new file current */
  123.  
  124.     /* "take" filename.rc */
  125.     (void) take_file(filename, top_level_parser, FALSE);
  126.  
  127.     if (cf) do_flagged();
  128.  
  129.     return true;
  130. }
  131.  
  132. /*
  133.  * do_flagged:
  134.  * display flagged messages of new file
  135.  */
  136. do_flagged()
  137. {
  138.     int i,count;
  139.     extern FILE *header_pipe;
  140.     FILE *more_pipe_open();
  141.  
  142.     if (!display_flagged_messages) 
  143.     return;
  144.     /* first find out how many */
  145.     for (count = 0, i = 1; i <= cf->count; i++) {
  146.     if (cf->msgs[i].flags & M_FLAGGED)
  147.         ++count;
  148.     }
  149.  
  150.     if (count >= cmcsb._cmrmx)
  151.     header_pipe = more_pipe_open(cmcsb._cmoj);
  152.     else
  153.     header_pipe = cmcsb._cmoj;
  154.     if (header_pipe == nil)
  155.     header_pipe == stdout;
  156.     header_print(0);
  157.     for (i = 1; i <= cf->count; i++) {
  158.     if (cf->msgs[i].flags & M_FLAGGED)
  159.         header_print(i);        /* uses header_pipe */
  160.     }
  161.     header_print(-1);
  162.     if (header_pipe == cmcsb._cmoj){
  163.     if (cmcsb._cmoj)
  164.         fflush(cmcsb._cmoj);    /* didn't open the pipe */
  165.     }
  166.     else if (header_pipe != stdout)
  167.     more_pipe_close(header_pipe);
  168.     header_pipe = NULL;
  169. }
  170.  
  171. /*
  172.  * getfile:
  173.  * open the file, initialize the msgvec structure
  174.  */
  175. msgvec *
  176. getfile (file, flag)
  177. char *file;
  178. int flag;
  179. {
  180.     msgvec *nf;
  181.     int type;
  182.     extern int num_msg_ops;        /* how many mail formats we know */
  183.     extern int auto_create_files;    /* do we need to ask? */
  184.     int probeval;
  185.     int err;
  186.     char **q;
  187.     int fd;
  188.  
  189.     if (same_file (file, mail_file))
  190.     for (q = &(incoming_mail[0]); q && *q; q++)
  191.         if (same_file (file, *q)) {
  192.         fprintf(stderr,"Cannot read a primary mail file that is also an incoming mail file.");
  193.         return(NULL);
  194.         }
  195.  
  196.     switch (probeval = mail_probe (file, &type)) /* can we read this? */
  197.     {
  198.     case PR_NAME:
  199.     fprintf (stderr,"Badly formed filename: %s", file);
  200.     return(NULL);
  201.     case PR_NOEX:
  202.     if (flag == CMD_EXAMINE) {
  203.         fprintf(stderr,"File not found: %s", file);
  204.         return(NULL);
  205.     }
  206.     fprintf (stderr, "File does not exist: %s\n", file);
  207.     if ((auto_create_files == SET_NO) || 
  208.         (auto_create_files == SET_ASK && (cmcsb._cmflg & CM_ITTY) &&
  209.          !yesno("Do you want to create it? ", "yes")))
  210.         return(NULL);
  211.     fd = creat(file, new_file_mode); /* XXX assuming local file */
  212.     if (fd >= 0)            /* error msg on open later */
  213.         close(fd);
  214.     /* ** fall through ** */
  215.     case PR_EMPTY:
  216.     for (type = 0; type < num_msg_ops; type++)
  217.         if (strcmp(default_mail_type.current,msg_ops[type].typename) == 0)
  218.         break;            /* use their default, if okay */
  219.     if (type == num_msg_ops)    /* bad typename */
  220.         type = TYPE_MTXT;        /* pick some type */
  221.     break;
  222.     case PR_PERM:
  223.     fprintf(stderr,"Cannot read file: %s\n", file);
  224.     return(NULL);
  225.     case PR_NOTOK:
  226.     fprintf(stderr,"File is damaged or in unknown format: %s", file);
  227.     return(NULL);
  228.     }
  229.     nf = (msgvec *) malloc (sizeof (msgvec)); /* room to put all this stuff */
  230.     if (!nf) {
  231.     fprintf(stderr,"Out of memory");
  232.     return(NULL);
  233.     }
  234.  
  235.     bzero(nf, sizeof (msgvec));        /* et voila, ne plus de core dumps */
  236.  
  237.     nf->type = type;
  238.  
  239.     strcpy (nf->filename, file);
  240.     nf->flags = ((flag == CMD_GET) ? 0 : MF_RDONLY); /* only flag so far */
  241.     if (same_file(file, mail_file))    /* primary mailbox is same file */
  242.     nf->flags |= MF_MAILBOX;
  243.  
  244.     nf->count = 0;
  245.     if (probeval == PR_NOEX) {        /*open file, interactive errors*/
  246.     nf->msgs = NULL;        /* nothing there */
  247.     err = (*msg_ops[nf->type].open)(nf,OP_APP|OP_INT);
  248.     }
  249.     else
  250.     err = (*msg_ops[nf->type].open)(nf,OP_INT);
  251.     if (err != 0) {
  252.     if (flag == CMD_GET && ((errno == EACCES)
  253. #ifdef EWOULDBLOCK
  254.         || (errno == EWOULDBLOCK)
  255. #endif /* EWOULDBLOCK */
  256.         )) {
  257.         fprintf (stderr, "\
  258.  Use the EXAMINE command if you want to open it in read-only mode.\n");
  259.         return(NULL);
  260.     }
  261.     return (NULL);
  262.     }
  263.  
  264.     if (nf->filep == NULL) {        /* reuse cf */
  265.     cf->flags |= MF_RDONLY;        /* downgrade file */
  266.     free (nf);
  267.     nf = cf;            /* flag that we are the same */
  268.     }
  269.     printf ("%d message%s read\n",nf->count,((nf->count==1) ? "" : "s"));
  270.  
  271.     record_mtime (nf);            /* save modify time for this file */
  272.  
  273.     nf->current = nf->count;        /* point to last message */
  274.     if (nf != cf) {            /* downgrade.  did not reread file */
  275.     time (&nf->when_read);
  276.     if (!sequence_init (nf))
  277.         cmerr ("Out of memory");
  278.     }
  279.     return (nf);
  280. }
  281.  
  282.  
  283. /* 
  284.  * mail_probe:
  285.  * probe the file to see what mail format it's in
  286.  * pass back probe error code, set typep if PR_OK
  287.  */
  288. int
  289. mail_probe (file, typep)
  290. char *file;
  291. int *typep;
  292. {
  293.     int stat, newstat;
  294.     extern int num_msg_ops;        /* how many formats we know */
  295.  
  296.     stat = PR_NAME;            /* assume the lowest level problem */
  297.     for (*typep = 0; *typep < num_msg_ops; (*typep)++) { /* test each type */
  298.     if ((newstat = (*msg_ops[*typep].probe)(file)) == PR_OK)
  299.         return (PR_OK);        /* that's the one */
  300.     if (newstat > stat)        /* did we get any further? */
  301.         stat = newstat;
  302.     }
  303.     return (stat);            /* tell them how we did */
  304. }
  305.  
  306.  
  307. /*
  308.  * get the size of a local file
  309.  */
  310. local_get_size(mail)
  311. msgvec *mail;
  312. {
  313.     struct stat sbuf;
  314.     if (fstat(fileno(mail->filep), &sbuf) == 0)
  315.     mail->size = sbuf.st_size;
  316.     else {
  317.     fprintf(stderr,"?Couldn't determine size of %s\n", mail->filename);
  318.     mail->size = 0;
  319.     }
  320. }
  321.  
  322. /*
  323.  * local_contig_open:
  324.  * Contiguous local files are opened the same way, regardless of format...
  325.  */
  326. local_contig_open (mail,flags)
  327. msgvec *mail;
  328. int flags;
  329. {
  330.     FILE *fp;
  331.     char *openflags;            /* how to open the file */
  332.     int err;
  333.     struct stat sbuf;
  334.     int lockable;            /* can we lock it? */
  335.     int upgrade = FALSE;
  336.     buffer bfile;
  337.  
  338.     if (flags & OP_APP)            /* gonna save to this file */
  339.     openflags = "a";        /* write only at end */
  340.     else if (mail->flags & MF_RDONLY)
  341.     openflags = "r";
  342.     else
  343.     openflags = "r+";
  344.  
  345.     /* if #filename# exists, we were in the middle of modifying it
  346.      * (probably the system crashed) */
  347.     if (!(flags & OP_PND)) {        /* poundfile unexpected */
  348.     poundfile (bfile, mail->filename);
  349.     if (access (bfile, F_OK) == 0) { /* #filename# exists */
  350.         if (gone (mail->filename)) { /* but no "filename" */
  351.         fprintf (stderr, "%s does not exist, but backup does\n",
  352.              mail->filename);
  353.         if (mail->flags & MF_RDONLY) { /* examine, don't change */
  354.             fprintf (stderr, "Examining %s backup file instead\n",
  355.                  bfile);
  356.             strcpy (mail->filename, bfile);
  357.         }
  358.         else {
  359.             fprintf (stderr, "Recovering from %s backup file...", 
  360.                  bfile);
  361.             fflush (stderr);
  362.             if (rename (bfile, mail->filename) != 0) {
  363.             fprintf (stderr, "rename failed, call a consultant\n");
  364.             return (PR_PERM);
  365.             }
  366.             fprintf (stderr, "done\n");    /* now file exists */
  367.         }
  368.         }
  369.         else {            /* #filename# *and* filename */
  370.         fprintf (stderr, "%s exists.\n\
  371.  Apparently MM had trouble while writing out %s.\n\
  372.  Please contact the consultant (send mail to bug-mm or consultant).\n\
  373.  Until you resolve this, you will not be able to modify this file, but you\n\
  374.  can EXAMINE it.\n", bfile, mail->filename);
  375.         if (mail->flags & MF_MAILBOX)
  376.             fprintf (stderr, "\
  377.  Since this file is your primary mailbox, you must resolve this\n\
  378.  problem before MM can process new mail.\n");
  379.         mail->flags |= MF_RDONLY; /* get it read-only */
  380.         }
  381.     }
  382.     }
  383.     
  384.     /* be careful about opening a file that's already open */
  385.     if (cf && same_file (mail->filename, cf->filename)) {
  386.     if (cf->flags & MF_RDONLY) {    /* current file is read only */
  387.         if (mail->flags & MF_RDONLY) { /* r -> r */
  388.         if (flags & OP_INT)
  389.             fprintf (stderr, "(Rereading %s)\n", mail->filename);
  390.         }
  391.         else {            /* r -> w */
  392.         upgrade = TRUE;        /* open and lock */
  393.         }
  394.     }
  395.     else {                /* current file is read/write */
  396.         if (mail->flags & MF_RDONLY) { /* w -> r */
  397.         unlock_file (cf->filename, fileno(cf->filep));
  398.         mail->filep = NULL;    /* reuse cf since same file */
  399.         return (0);
  400.         }
  401.         else {            /* w -> w */
  402.         if (flags & OP_INT)
  403.             fprintf (stderr, 
  404.                  "?%s already current writable mail file\n", 
  405.                  mail->filename);
  406.         free (mail);
  407.         errno = ETXTBSY;
  408.         return (ETXTBSY);
  409.         }
  410.     }
  411.     }    
  412.  
  413.     if (!upgrade && (flags & OP_APP) || ((mail->flags & MF_RDONLY) == 0)) {
  414.     if (stat (mail->filename, &sbuf) != 0) {
  415.         /*
  416.          * if filename doesn't exist, make sure it gets created
  417.          * with the proper mode.
  418.          */
  419.         int fd = creat (mail->filename, new_file_mode);
  420.         if (fd < 0) {
  421.         fprintf (stderr, "Cannot create %s: %s\n", mail->filename,
  422.              errstr (errno));
  423.         (void) free (mail);    /* XXX bogus! */
  424.         return errno;
  425.         }
  426.         else
  427.         (void) close (fd);
  428.     }
  429.     }
  430.     
  431.     if ((fp = fopen (mail->filename, openflags)) == NULL) {
  432.         if (flags & OP_INT) {        /* interactive? */
  433.         fprintf (stderr, "?Cannot open %s: %s\n", 
  434.              mail->filename, errstr (errno));
  435.     }
  436.     free (mail);
  437.     return (errno);
  438.     }
  439.  
  440.     if (fstat(fileno(fp), &sbuf) == 0) {
  441.     mail->size = sbuf.st_size;
  442.     mail->mtime = sbuf.st_mtime;    /* record_mtime(mail) */
  443.     lockable = ((sbuf.st_mode & S_IFMT) == S_IFREG);
  444.     }
  445.     else {
  446.     fprintf(stderr,"fstat: Couldn't determine size and type of %s\n",
  447.         mail->filename);
  448.     mail->size = 0;
  449.     lockable = FALSE;
  450.     }
  451.  
  452.     if (upgrade) {            /* close first since we are  */
  453.     local_close (cf->filep);    /* about to lock.  too late */
  454.     cf->filep = NULL;        /* if lock succeeds */
  455.     }
  456.     /* if opened for write, lock the file away */
  457.     if (lockable && ((flags & OP_APP) || !(mail->flags & MF_RDONLY))) {
  458.     if (lock_file (mail->filename, fileno (fp)) < 0) {
  459.         if (errno == EINVAL) {
  460.         if (debug)
  461.             fprintf (stderr, "%%File %s could not be locked\n",
  462.                  mail->filename);
  463.         /*
  464.          * pretend we locked it, and hope for the best
  465.          */
  466.         }
  467.         else {
  468.         if (!upgrade)
  469.             local_close(fp);    /* close it again */
  470.         else {
  471.             cf->filep = fp;    /* use this instead */
  472.         }
  473.         if (!(flags & OP_INT)) {
  474.             free (mail);
  475.             return (errno);
  476.         }
  477.         fprintf (stderr, "?Can't lock %s: %s.\n\
  478.  Apparently some other process is accessing this file in read/write mode.\n",
  479.              mail->filename,
  480. #ifdef EWOULDBLOCK
  481.              (errno == EWOULDBLOCK) ? "file is busy" :
  482. #endif
  483.              errstr (errno));
  484.         free (mail);
  485.         return (errno);
  486.         }
  487.     }
  488.     }
  489.  
  490.     mail->filep = fp;
  491.     if (! (flags & OP_APP))        /* don't count if for append */
  492.         local_contig_count (mail);    /* count 'em */
  493.     return 0;                /* no error */
  494. }
  495.  
  496.  
  497. /*
  498.  * local_contig_count:
  499.  * count the messages in this mail file.  For formats which call this
  500.  * routine, (ones which are local, contiguous mail files), this
  501.  * requires reading them all in, so do that and keep in as many as we
  502.  * have room for.  This fills in mail->count and mail->msgs.
  503.  */
  504. local_contig_count (mail)
  505. msgvec *mail;
  506. {
  507.     int alloccnt;            /* how many we've allocated */
  508.     int err;
  509.  
  510.     alloccnt = mail->size / 1000;
  511.     if (alloccnt < 10)
  512.     alloccnt = 10;            /* start with ten messages */
  513.     mail->count = 0;            /* none yet */
  514.     /* get space for headers */
  515.     mail->msgs = (message *) malloc (alloccnt * sizeof (message));
  516.     mail->last_read = 0;        /* haven't read any yet */
  517.     mail->keywords = nil;
  518.     /* read the next one as long as we can */
  519.     while ((err = (*msg_ops[mail->type].rd_msg)(mail, ++mail->count)) == 
  520.        RD_OK) { 
  521.     mail->msgs[mail->count].hdrsum = NULL; /* not used yet */
  522.         if (mail->count+1 == alloccnt)    /* filled 'em all up (except #0) */
  523.         mail->msgs = (message *)
  524.             realloc (mail->msgs, (alloccnt+=100) * sizeof (message));
  525.     }
  526.     /* read 1:n-1, failed on n -> n-1 msgs */
  527.     mail->count--;
  528.  
  529.     if (err == RD_FAIL) {        /* format problem */
  530.     if (!(mail->flags & MF_RDONLY)) {
  531.         mail->flags |= MF_RDONLY;    /* make file read-only */
  532.         unlock_file(mail->filename, fileno(mail->filep)); /* unlock it */
  533.     }
  534.  
  535.     fprintf (stderr, "\n\
  536. There is a problem with %s.\n\
  537. MM was unable to read the entire mail file because it appears to have\n\
  538. been corrupted.  You may be able to read some of the messages in this file,\n\
  539. but MM will not add messages to it or allow you to modify it.  See a\n\
  540. consultant or systems administrator for help.\n\n",
  541.          mail->filename);
  542.     if (mail->flags & MF_MAILBOX)
  543.         fprintf (stderr, "\
  544. Since this file is your primary mailbox, you must resolve this\n\
  545. problem before MM can process new mail.  If no assistance is available\n\
  546. now, rename the file so MM won't find it at startup, and a new mailbox\n\
  547. file will be created for you the next time you invoke MM.\n\n");
  548.     }
  549.     /* snug it down to fit just right */
  550.     mail->msgs = (message *) realloc (mail->msgs, 
  551.                       (mail->count+1)*sizeof (message));
  552. }
  553.  
  554. /*
  555.  * local_contig_proben:
  556.  * open a local contiguous file for probin'
  557.  * probe-open = proben -- get it??
  558.  */
  559. int
  560. local_contig_proben (file,fpp)
  561. char *file;
  562. FILE **fpp;
  563. {
  564.     buffer bfile;
  565.     char *f;
  566.  
  567.     f = file;
  568.     if (gone (file)) {            /* file doesn't exist, or is empty */
  569.     poundfile (bfile, file);
  570.     if (access (bfile, F_OK) == 0) { /* but #file# does exist! */
  571.         f = bfile;            /* so look there */
  572.     }
  573.     }
  574.     if ((*fpp = fopen (f, "r")) == NULL)
  575.         switch (errno) {        /* nice switch */
  576.     case EACCES:
  577.         return (PR_PERM);        /* couldn't look at it */
  578.     default:
  579.         return (PR_NOEX);        /* say it doesn't exist */
  580.     }
  581.     return (PR_OK);            /* okay, we opened it */
  582. }
  583.  
  584. /*
  585.  * fail_msg:
  586.  * send a nice printf that we failed on this message
  587.  */
  588. fail_msg(num)
  589. int num;
  590. {
  591.     if (num > 1)
  592.     fprintf (stderr, "\
  593. ?File is corrupted near message %d; the rest of file will be ignored.\n",
  594.          num - 1);
  595.     else
  596.     fprintf (stderr, "?Mail file is corrupted.\n");
  597. }
  598.  
  599.  
  600. /*
  601.  * local_close:
  602.  * local files are also mostly closed the same way
  603.  * gotta free up the lock, though that probably gets done automagically
  604.  * when we let go of the file...
  605.  */
  606. local_close (fp)
  607. FILE *fp;
  608. {
  609.     if (fp != NULL)
  610.     fclose (fp);
  611. }
  612.  
  613.  
  614. /*
  615.  * fgetline:
  616.  * Keep doing fgets till we get the whole line.
  617.  * on end of file, return what we have, or NULL if we have nothing
  618.  */
  619. char *
  620. fgetline (filep)
  621. FILE *filep;
  622. {
  623.     int buflen;
  624.     char *buf;
  625.     char *bufp;
  626.  
  627.     buflen = 1;                /* start with room for NULL :-) */
  628.     bufp = buf = malloc (buflen+=100);    /* need some space to start */
  629.     while (true) {            /* till we return */
  630.         if (fgets (bufp, 101, filep) == NULL) { /* get the line */
  631.         if (bufp == buf) {        /* should be end of file */
  632.         free (buf);
  633.         return (NULL);
  634.         }
  635.         else {            /* read some before EOF */
  636.         *bufp = '\0';        /* close it off */
  637.         return (buf);
  638.         }
  639.     }
  640.     if ((bufp = index (bufp, '\n')) != NULL) { /* get it? */
  641.         *bufp = '\0';        /* we don't need the CR */
  642.         return (buf);        /* return the whole line */
  643.     }
  644.     buf = (char *) realloc (buf,buflen+=100); /* get more space */
  645.     bufp = &buf[buflen-101];    /* point to null of what we have */
  646.     }
  647. }
  648.  
  649.  
  650. /*
  651.  * cmd_check:
  652.  * check for new messages in the current mail file
  653.  */
  654. cmd_check (n)
  655. int n;
  656. {
  657.     noise ("for new mail");
  658.     confirm ();
  659.     if (new_mail(false) == false)
  660.     printf("No new mail.\n");
  661. }
  662.  
  663.  
  664. /*
  665.  * update and update_1:
  666.  * Write out the current version of the mail, saving the old version
  667.  * in mailname~.  Close old file.  If writing to an alternate file,
  668.  * (name already in pf->filename) don't write a backup.
  669.  *
  670.  * Note that the msgvec isn't updated to reflect the deleted messages,
  671.  * since if we're about to exit, there's no point.
  672.  *
  673.  * If file is now empty and wasn't before (and isn't main mail file),
  674.  * delete it and free the msgvec.
  675.  *
  676.  * We cannot allow interrupts during update, since most of them want
  677.  * to call update, and also we really don't want to stop in the middle
  678.  * of writing the file.  So, we defer them until we're done (and
  679.  * MF_DIRTY ensures we won't redo the update).  
  680.  *
  681.  * Return true on success, false if we fail anywhere.
  682.  *
  683.  * Note: update() calls check_mtime(), which calls update_1()
  684.  *       (we didn't want to call update() from inside itself)
  685.  */
  686.  
  687. int
  688. update (pf,updflags)
  689. msgvec **pf;
  690. int updflags;
  691. {
  692.     int err;
  693.  
  694.     if (!(updflags & UPD_ALTFILE)) {    /* don't check altfile */
  695.     if (!check_mtime(*pf, &err))    /* didn't pass the check */
  696.         return (err);
  697.     }
  698.     update_1 (pf, updflags);
  699. }
  700.  
  701. int
  702. update_1 (pf,updflags)
  703. msgvec **pf;
  704. int updflags;
  705. {
  706.     msgvec *f;                /* get a non-pointered */
  707.     int i, err;
  708.     FILE *old_fp;
  709.     buffer ofile, bfile;
  710.     int backup;
  711.     int flags;                /* flags to send to wr_msg */
  712.     int empty;                /* is the file empty? */
  713.     int doit = FALSE;            /* do we need to do an update? */
  714.     unsigned short filemode = new_file_mode;
  715.  
  716.     int exp = updflags & UPD_EXPUNGE;    /* should we do an expunge? */
  717.     int savemods = updflags & UPD_SAVEMOD; /* save even if only MF_MODIFED */
  718.     int always = updflags & UPD_ALWAYS;    /* write it out no matter what */
  719.     int altfile = updflags & UPD_ALTFILE; /* write to an alternate file */
  720.     int quiet = updflags & UPD_QUIET;    /* be quiet */
  721.  
  722.     int result = true;            /* assume we'll succeed */
  723.     long mask = block_signals ();    /* block out interrupts */
  724.  
  725.     if (pf == 0)
  726.     goto failed;
  727.  
  728.     f = *pf;
  729.     if (f == 0)
  730.     goto failed;
  731.  
  732.     if ((f->flags & MF_RDONLY) && !altfile && !(f->flags & MF_SAVEONLY))
  733.     goto failed;
  734.  
  735.     if (exp) {                /* check for deletions to expunge */
  736.     for (i = 1; i <= f->count && !doit; i++)
  737.         if (f->msgs[i].flags & M_DELETED)
  738.         doit = TRUE;
  739.     if (doit)            /* expunge! */
  740.         printf ("Expunging deleted messages.\n");
  741.     else
  742.         printf ("No messages deleted.\n");
  743.     }
  744.  
  745.     if (always ||            /* forced to do it */
  746.     (f->flags & MF_DIRTY) ||    /* needs saving */
  747.     ((f->flags & MF_MODIFIED) && savemods)) /* want to save flags */
  748.     doit = TRUE;
  749.  
  750.     if (!doit && !altfile)
  751.     goto failed;
  752.  
  753.     /****** use msg_ops when we add the rename routine to it */
  754.     {
  755.     struct stat sb;
  756.     if (stat (f->filename, &sb) == 0)
  757.         filemode = sb.st_mode & 07777;
  758.  
  759.     poundfile (bfile, f->filename);    /* make #filename# */
  760.     sprintf (ofile, "%s~", f->filename); /* and filename~ */
  761.     if (backup = (rename (f->filename, bfile) != 0)) {
  762.         switch(errno) {
  763.         case ENOENT:
  764.         if (!quiet)
  765.             fprintf (stderr,"%s does not exist.  Creating new file.\n",
  766.                  f->filename);
  767.         break;
  768. #ifndef HAVE_FLEXFILENAMES
  769.         case EINVAL:
  770.         if (!quiet)
  771.             fprintf (stderr, "Filename too long for rename: %s\n\
  772. Use the WRITE or COPY commands to save your messages.", f->filename);
  773.         goto failed;
  774. #endif
  775.         default:
  776.         if (!quiet)
  777.             fprintf (stderr,"Can't rename %s to %s: %s\n\
  778.  Use the WRITE or COPY commands to save your messages.",
  779.                  f->filename, bfile, errstr (errno));
  780.         goto failed;
  781.         }
  782.     }
  783.     unlink(ofile);            /* delete filename~ to make room */
  784.     }
  785.     old_fp = f->filep;            /* don't overwrite that */
  786.     /* now we've saved (or closed) the old file, open the new one */
  787.     err = (*msg_ops[f->type].open)(f, OP_APP|OP_PND); /* new file, append */
  788.  
  789.     if (!err)
  790.     if (chmod (f->filename, filemode) != 0)
  791.         if (!quiet)
  792.         perror ("update: error updating file modes");
  793.  
  794.     flags = exp ? (WR_SAVE|WR_EXP) : WR_SAVE;
  795.     if (f->count == 0)
  796.     empty = FALSE;          /* if they started "empty", leave it alone */
  797.     else
  798.     empty = TRUE;
  799.     if (!err) {
  800.         for (i = 1, err = 0; (i <= f->count) && (err == 0); i++) {
  801.         if (!exp || !(f->msgs[i].flags & M_DELETED))
  802.         empty = FALSE;
  803.         err=(*msg_ops[f->type].wr_msg)(f,&f->msgs[i],i,flags);
  804.         if (err) err = errno;
  805.     }
  806.     if (!err)            /* again, in case file had 4000 bit */
  807.         if (chmod (f->filename, filemode) != 0)
  808.         if (!quiet)
  809.             perror ("update: error updating file modes");
  810.     /* XXX This should be done through a call via msg_ops XXX */
  811.     if (fsync(fileno(f->filep)) < 0)
  812.         perror (f->filename);
  813.     }
  814.     if (err) {                /* trouble writing file */
  815.         (*msg_ops[f->type].close)(f->filep); /* close failed file */
  816.     f->filep = old_fp;        /* replace good one */
  817.     if (rename (bfile, f->filename) != 0) {
  818.         if (!quiet)
  819.         fprintf(stderr, "\
  820. Can't write output file %s and can't restore from %s - %s\n",
  821.             f->filename, bfile, errstr (err));
  822.     }
  823.     else {
  824.         if (!quiet)
  825.         fprintf (stderr,"\
  826. File not saved: can't write output file - %s\n",
  827.              errstr (err));
  828.     }
  829.     if (!altfile) {            /* don't set this for another file */
  830.         cf->flags |= MF_WRITERR;
  831.         record_mtime (f);
  832.     }
  833.     errno = err;
  834.     goto failed;
  835.     }
  836.     if (altfile) {
  837.     (*msg_ops[f->type].close)(f->filep); /* success, close the file */
  838.     f->filep = old_fp;        /* restore original file */
  839.     goto succeeded;
  840.     }
  841.     
  842.     if (empty) {            /* nothing left, delete it */
  843.     if (f->flags & MF_MAILBOX) {
  844.         if (!quiet)
  845.         fprintf(stderr,
  846.             "All messages deleted, not deleting main mail file\n");
  847.     }
  848.     else {
  849.         if (!quiet)
  850.         fprintf (stderr, "All messages deleted, deleting file...");
  851.         /****************/
  852.         if (unlink (f->filename) != 0) { /* delete it */
  853.         if (!quiet)
  854.             fprintf (stderr,"Can't delete %s - %s\n", 
  855.                  f->filename, errstr (errno));
  856.         goto failed1;
  857.         }
  858.         /****************/
  859.         if (!quiet)
  860.         fprintf (stderr, "OK\n");
  861.         (*msg_ops[f->type].close)(f->filep); /* close old file */
  862.         msgvec_free (f);        /* free all the bits and pieces */
  863.         *pf = nil;            /* no more file */
  864.         goto succeeded;
  865.     }
  866.     }
  867.     f->flags &= ~(MF_DIRTY|MF_MODIFIED); /* not dirty any more */
  868.     (*msg_ops[f->type].close)(old_fp);    /* success, close old file */
  869.  
  870.     record_mtime (f);            /* update saved modify time */
  871.     cf->flags &= ~(MF_WRITERR|MF_SAVEONLY);
  872.     goto succeeded;
  873.  
  874.   failed:
  875.     result = false;
  876.     release_signals (mask);
  877.     return result;
  878.   failed1:
  879.     result = false;
  880.     release_signals (mask);
  881.  
  882.   succeeded:
  883.     /* the last rename worked, so this should too.  if it doesn't,
  884.      * what could we do anyway?  maybe unlink (bfile)??  Naah.
  885.      */
  886.     rename(bfile, ofile);
  887.     release_signals (mask);
  888.     return result;
  889. }
  890.  
  891.  
  892. /*
  893.  * apnd_update:
  894.  * write out the new messages (the last cnt messages)
  895.  */
  896.  
  897. int
  898. apnd_update(pf, cnt)
  899. msgvec **pf;
  900. int cnt;
  901. {
  902.     int i, err = 0;
  903.     off_t fsize;
  904.     msgvec *f = *pf;            /* to be consistent with update() */
  905.  
  906.     local_get_size(f);
  907.     fsize = (off_t) f->size;        /* remember orig size for error cond */
  908.  
  909.     for (i = f->count + 1 - cnt; (i <= f->count) && (err == 0); i++)
  910.     err = (*msg_ops[f->type].wr_msg)(f, &f->msgs[i], i, WR_SAVE);
  911.  
  912.     if (err) {                /* put back file on disk to previous */
  913.     f->flags |= MF_WRITERR;        /*   state */
  914.     ftruncate(fileno(f->filep), fsize); /* XXX low level */
  915.     fsync(fileno(f->filep));    /* XXX low level */
  916.     fseek(f->filep, 0, SEEK_END);    /* go to the end */ /* XXX low level */
  917.     f->size = fsize;
  918.     }
  919.  
  920.     record_mtime(f);            /* remember when we last touched it */
  921.     return(!err);
  922. }
  923.  
  924.  
  925. /*
  926.  * msgvec_free:
  927.  * Free all the various pieces of the msgvec we have here.
  928.  * We don't null out the pointer though...
  929.  */
  930. msgvec_free (mv)
  931. msgvec *mv;
  932. {
  933.     int i;
  934.     char *text;
  935.     keylist keys;
  936.  
  937.     sequence_free (mv);            /* free any sequencing info */
  938.     for (i = 1; i <= mv->count; i++) {    /* free the message */
  939.     if ((text = mv->msgs[i].text) != NULL) /* the text */
  940.         safe_free (text);
  941.     if ((keys = mv->msgs[i].keywords) != NULL) /* keywords */
  942.         free_keylist(keys);
  943.     if ((text = mv->msgs[i].from) != NULL) /* from field */
  944.         safe_free(text);
  945.     }
  946.     free_keylist(mv->keywords);
  947.     safe_free (mv->msgs);        /* and the message structures */
  948.     safe_free (mv);            /* the data structure itself */
  949. }
  950.  
  951. /*
  952.  * record the mtime value for a mail file, so we can detect later on if
  953.  * some other process has changed the file.
  954.  */
  955. record_mtime (mf)
  956. msgvec *mf;
  957. {
  958.     struct stat sb;
  959.     if (stat (mf->filename, &sb) != 0) {
  960.     perror (mf->filename);
  961.     mf->mtime = 0;
  962.     }
  963.     else
  964.     mf->mtime = sb.st_mtime;
  965. }
  966.  
  967.  
  968. /* 
  969.  * check_mtime
  970.  * compare file's on-disk modified time with the one we recorded.
  971.  * if file on disk has changed:
  972.  *   warn the user, rename filename to filename.<pid>
  973.  *   unset mail-file, if set
  974.  *   if in core version was not modified, change to RDONLY
  975.  *   if modified, try to save
  976.  * return TRUE if we the mtime is unchanged (okay), FALSE otherwise
  977.  *     fill in upd_result with the result from update, if non-NULL
  978.  */
  979.  
  980. int
  981. check_mtime(mf, upd_result)
  982. msgvec *mf;
  983. int *upd_result;
  984. {
  985.     struct stat sb;
  986.     int err;
  987.     char pidstr[10];
  988.     string oldfile;
  989.     extern int handle_changed_modtime;
  990.  
  991.     if (!mf || (mf->flags & MF_RDONLY)) 
  992.     return (TRUE);            /* no need to check */
  993.  
  994.     err = stat (mf->filename, &sb) != 0;
  995.     if (!err && (sb.st_mtime == mf->mtime))
  996.     return (TRUE);            /* everything fine */
  997.  
  998.     /* uh oh! */
  999.     fprintf (stderr, "\nWarning: %s has changed on disk!\n",
  1000.          mf->filename);
  1001.  
  1002.     if ((handle_changed_modtime == SET_NO) || 
  1003.     (handle_changed_modtime == SET_ASK && (cmcsb._cmflg & CM_ITTY) &&
  1004.      !yesno("Attempt to recover? ", "yes"))) {
  1005.     record_mtime(mf);        /* don't nag them again for this mod */
  1006.     return(TRUE);            /* they said forget it */
  1007.     }
  1008.  
  1009.     /* rename current file to filename.pid */
  1010.     mf->flags &= ~MF_MAILBOX;        /* no longer primary mail file */
  1011.     sprintf (pidstr, "%d", PID);    /* stringize */
  1012.     strcpy(oldfile, mf->filename);    /* save old name */
  1013.     strcat(mf->filename, ".");
  1014.     strcat(mf->filename, pidstr);
  1015.     fprintf(stderr, "Renaming current (internal) version to %s (read-only)\n", 
  1016.          mf->filename);
  1017.     fprintf(stderr, 
  1018.         "Please exit MM and resolve any conflicts between %s and %s\n",
  1019.         oldfile, mf->filename);
  1020.     fprintf (stderr, "Contact a consultant if you need assistance\n");
  1021.     mf->flags |= MF_RDONLY|MF_SAVEONLY;    /* make it read-only, but updateable */
  1022.     modify_read_only = SET_ASK;        /* annoy them */
  1023.  
  1024.     err = update_1(&mf, UPD_ALWAYS);
  1025.  
  1026.     if (upd_result)            /* maybe they don't care */
  1027.     *upd_result = err;
  1028.     return (FALSE);
  1029. }
  1030.  
  1031.  
  1032. /*
  1033.  * poundfile:
  1034.  * make #filename# or /path/#filename#
  1035.  */
  1036. poundfile (buf, fname)
  1037. char *buf, *fname;
  1038. {
  1039.     char *cp;
  1040.  
  1041.     cp = rindex (fname, '/');
  1042.     if (cp == 0)            /* no pathname, easy */
  1043.     sprintf (buf, "#%s#", fname);
  1044.     else {                /* slip # in */
  1045.     strcpy (buf, fname);
  1046.     sprintf (buf + (cp - fname) + 1, "#%s#", cp + 1);
  1047.     }
  1048. }
  1049.  
  1050. /*
  1051.  * make_backup_file:
  1052.  * (apparently unused)
  1053.  * try to rename "file" to "file~" or "#file#"
  1054.  * return filename used or NULL for failure
  1055.  */
  1056. char *
  1057. make_backup_file (filename)
  1058. char *filename;
  1059. {
  1060.     int err;
  1061.     char *cp;
  1062.     char backup[MAXPATHLEN];
  1063.  
  1064.     sprintf (backup, "%s~", filename);
  1065.  
  1066.     err = rename (filename, backup);
  1067.     if (err < 0) {
  1068.     poundfile (backup, filename);
  1069.     err = rename (filename, backup);
  1070.     }
  1071.     
  1072.     if (err < 0)
  1073.     return 0;
  1074.  
  1075.     if (cp = malloc (strlen (backup) + 4)) {
  1076.     strcpy (cp, backup);
  1077.     return cp;
  1078.     }
  1079.     return 0;
  1080. }
  1081.  
  1082.  
  1083. /*
  1084.  * given a new filename to open and a file pointer for a file we already
  1085.  * have open, determine if they are the same file.
  1086.  * returns TRUE if they are the same, FALSE if different or an error
  1087.  * (error in errno).
  1088.  * XXX Need to make this format specific.  For now only works with
  1089.  * XXX local files.
  1090.  */
  1091.  
  1092. int
  1093. same_file (name1, name2)
  1094. char *name1, *name2;
  1095. {
  1096.     struct stat buf1, buf2;
  1097.  
  1098.     if (stat (name1, &buf1) != 0) {
  1099.     return (FALSE);
  1100.     }
  1101.     if (stat (name2, &buf2) != 0) {
  1102.     return (FALSE);
  1103.     }
  1104.     if ((buf1.st_dev == buf2.st_dev) && (buf1.st_ino == buf2.st_ino))
  1105.     return (TRUE);
  1106.     return (FALSE);
  1107. }
  1108.  
  1109. /*
  1110.  * gone:
  1111.  * file gone: empty or nonexistent
  1112.  */
  1113. gone(file)
  1114. char *file;
  1115. {
  1116.     struct stat statb;
  1117.  
  1118.     if (access (file, F_OK) != 0)    /* it's gone */
  1119.     return (TRUE);
  1120.     if ((stat(file, &statb) == 0) && (statb.st_size == 0))
  1121.     return (TRUE);            /* it's empty */
  1122.     return (FALSE);
  1123. }
  1124.